
#ifndef __LOGGER_H__
#define __LOGGER_H__
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include<memory>
#include<cstring>
#include"looper.hpp"
#include<memory>
#include<unordered_map>
// //日志器模块:
// 功能: 对前边所有模块进行整合，向外提供接口完成不同等级日志的输出
// 管理的成员:
// 格式化模块对象
// 落地模块对象数组（一个日志器可能会向多个位置进行日志输出）
// 默认的日志输出限制等级（大于等于限制等级的日志才能输出）
// 互斥锁（保证日志输出过程是线程安全的，不会出现交叉日志）
// 日志器名称（日志器的唯一标识，以便于查找）
// 提供的操作:
// debug 等级日志的输出操作 (分别会封装日志消息 LogMsg-- 各个接口日志等级不同)
// info 等级日志的输出操作
// warn 等级日志的输出操作
// error 等级日志的输出操作
// fatal 等级日志的输出操作
// 实现:
// 抽象 Logger 基类（派生出同步日志器类 & 异步日志器类）
// 因为两种不同的日志器，只有落地方式不同，因此将落地操作给抽象出来。不同的日志器调用各自的落地操作进行日志落地。模块关联中使用基类指针对子类日志器对象进行日志管理和操作
namespace logsys {
class Logger {
public:
    using ptr = std::shared_ptr<Logger>;
    // 构造函数，接收日志器名称、日志等级、格式化器指针、日志落地模块指针列表作为参数
    Logger(const std::string &logger_name,
           LogLevel::value level,
           Formatter::ptr &formatter,
           std::vector<LogSink::ptr> &sinks) :
        _logger_name(logger_name),
        _limit_level(level),
        _formatter(formatter),
        _sinks(sinks.begin(), sinks.end()) {}
    const std::string &name(){return _logger_name; }
    /*
     * 完成构造日志消息对象并进行格式化，得到格式化后的日志消息字符串，然后进行落地输出
     * 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
     */
    void debug(const std::string &file, size_t line, const std::string &fmt, ...) {
        // 1. 判断当前的日志是否达到了输出等级
        if (LogLevel::value::DEBUG < _limit_level) { return; }
        // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
        va_list ap;
        va_start(ap, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), ap);
        if (ret == -1) {
            std::cout << "vasprintf failed!!\n";
            return;
        }
        va_end(ap); // 将ap指针置空
        serialize(LogLevel::value::DEBUG, file, line, res);
        free(res);
    }

    void info(const std::string &file, size_t line, const std::string &fmt, ...) {
        // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
        // 1. 判断当前的日志是否达到了输出等级
        if (LogLevel::value::INFO < _limit_level) { return; }
        // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
        va_list ap;
        va_start(ap, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), ap);
        if (ret == -1) {
            std::cout << "vasprintf failed!!\n";
            return;
        }
        va_end(ap); // 将ap指针置空
        serialize(LogLevel::value::INFO, file, line, res);
        free(res);
    }

    void warn(const std::string &file, size_t line, const std::string &fmt, ...) {
        // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
        // 1. 判断当前的日志是否达到了输出等级
        if (LogLevel::value::WARN < _limit_level) { return; }
        // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
        va_list ap;
        va_start(ap, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), ap);
        if (ret == -1) {
            std::cout << "vasprintf failed!!\n";
            return;
        }
        va_end(ap); // 将ap指针置空
        serialize(LogLevel::value::WARN, file, line, res);
        free(res);
    }

    void error(const std::string &file, size_t line, const std::string &fmt, ...) {
        // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
        // 1. 判断当前的日志是否达到了输出等级
        if (LogLevel::value::ERROR < _limit_level) { return; }
        // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
        va_list ap;
        va_start(ap, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), ap);
        if (ret == -1) {
            std::cout << "vasprintf failed!!\n";
            return;
        }
        va_end(ap); // 将ap指针置空
        serialize(LogLevel::value::ERROR, file, line, res);
        free(res);
    }

    void fatal(const std::string &file, size_t line, const std::string &fmt, ...) {
        // 通过传入的参数构造出一个日志消息对象，进行日志的格式化，最终落地
        // 1. 判断当前的日志是否达到了输出等级
        if (LogLevel::value::FATAL < _limit_level) { return; }
        // 2. 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
        va_list ap;
        va_start(ap, fmt);
        char *res;
        int ret = vasprintf(&res, fmt.c_str(), ap);
        if (ret == -1) {
            std::cout << "vasprintf failed!!\n";
            return;
        }
        va_end(ap); // 将ap指针置空
        serialize(LogLevel::value::FATAL, file, line, res);
        free(res);
    }

protected:
    void serialize(LogLevel::value level, const std::string &file, size_t line, char *str) {
        // 3. 构造LogMsg对象
        LogMsg msg(LogLevel::value::DEBUG, line, file, _logger_name, str);
        // 4. 通过格式化工具对LogMsg进行格式化，得到格式化后的日志字符串
        std::stringstream ss;
        _formatter->format(ss, msg);
        // 5. 进行日志落地
        log(ss.str().c_str(), ss.str().size());
    }

    /*
     * 抽象接口完成实际的落地输出，不同的日志器会有不同的实际落地方式
     * 这里是纯虚函数，需要派生类去实现具体的落地逻辑
     */
    virtual void log(const char *data, size_t len) = 0; 

protected:
    std::mutex _mutex;
    std::string _logger_name;
    std::atomic<LogLevel::value> _limit_level;
    Formatter::ptr _formatter;
    std::vector<LogSink::ptr> _sinks;
};

enum LoggerType{
    LOGGER_SYNC,
    LOGGER_ASYNC
};

class SyncLogger : public Logger {
public:
    // 构造函数，接收日志器名称、日志等级、格式化器指针、日志落地模块指针列表作为参数
    // 用于初始化SyncLogger对象，并调用基类Logger的构造函数进行相关初始化
    SyncLogger(const std::string &logger_name,
               LogLevel::value level,
               Formatter::ptr &formatter,
               std::vector<LogSink::ptr> &sinks) :
        Logger(logger_name, level, formatter, sinks) {}

protected:
    // 同步日志器，是将日志直接通过落地模块句柄进行日志落地
    // 该函数实现了将日志数据落地的具体逻辑，是同步日志器的核心方法
    void log(const char *data, size_t len) {
        // 使用std::unique_lock锁定互斥量_mutex，保证多线程环境下日志落地操作的线程安全
        std::unique_lock<std::mutex> lock(_mutex);
        // 如果日志落地模块列表_sinks为空，则直接返回，不进行日志落地操作
        if (_sinks.empty()) return;
        // 遍历日志落地模块列表_sinks
        for (auto &sink : _sinks) {
            // 调用每个日志落地模块的log函数，将日志数据data及其长度len传递过去，实现日志落地
            sink->log(data, len);
        }
    }
};

// 为了避免因为写日志的过程阻塞，导致业务线程在写日志的时候影响效率，因此异步的
// 思想就是不让业务线程进行日志的实际落地操作，而是将日志消息放到缓冲区（一块指定的内存）中。
// 接下来有一个专门的异步线程，去针对缓冲区中的数据进行处理（实际的落地操作）。

// 1、实现一个线程安全的缓冲区。
// 创建一个异步工作线程，专门负责缓冲区中日志消息的落地操作。
// 缓冲区详细设计
// 使用队列，缓存日志消息，逐条处理
// 2、要求：不能涉及空间的频繁申请与释放，否则会降低效率。
// 3、结果：设计一个环形队列（提前将空间申请好，然后对空间循环利用）。
// 环形队列有一个读指针放数据，一个写指针取出数据。
// 线程安全实现：对于缓冲区的读写加锁。因为写日志操作，在实际开发中，并不会分配太多资源，所以工作线程只需要一个日志器有一个就行。
// 涉及的锁冲突：生产者与生产者的互斥 & 生产者与消费者的互斥。由于所有线程之间都存在互斥关系，锁冲突较为严重。
// 4、解决方案：双缓冲区
// 业务线程将日志写入任务写入缓冲区，异步工作线程处理任务处理缓冲区中的任务。
// 进一步优化：如果缓冲区存的是 logmsg 的话，虽然交换缓冲区减少了锁冲突，但
//是在处理的时候还是会涉及到大量的构造和释放，很消耗效率。因此将格式化后的一条条字符串直接
//写到缓冲区里，然后可以直接针对一堆数据一次性做写磁盘的操作，大大减少 IO 的次数。


// 异步日志器设计:
// 继承于 Logger 日志器类
// 对于写日志操作进行函数重写（不再将数据直接写入文件，而是通过异步消息处理器，放到缓冲区中）
// 通过异步消息处理器，进行日志数据的实际落地
// 管理的成员:
// 异步工作器（异步消息处理器）
// 完成后，完善日志器建造者，进行异步日志器安全模式的选择，提供异步日志器的创建
class AsyncLogger : public Logger {
public:
    // 构造函数，接收日志器名称、日志等级、格式化器指针、日志落地模块指针列表、异步工作模式等参数
    // 调用基类Logger的构造函数进行初始化，并创建异步工作器
    // 使用std::bind将realLog函数与当前对象绑定，并设置参数占位符，传入异步工作器
    AsyncLogger(const std::string &logger_name,
                LogLevel::value level,
                Formatter::ptr &formatter,
                std::vector<LogSink::ptr> &sinks,
                AsyncType looper_type) :
        Logger(logger_name, level, formatter, sinks),
        _looper(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::realLog, this, std::placeholders::_1), looper_type)) {}

    // 重写日志写入函数，将数据写入异步工作器的缓冲区
    // 由于push操作本身是线程安全的，所以这里不需要额外加锁
    void log(const char *data, size_t len) {
        _looper->push(data, len);
    }

    // 实际落地函数，将缓冲区中的数据落地到各个日志落地模块
    // 因为异步工作器是串行的，所以不需要加锁保护，只需保证数据安全落入缓冲区
    void realLog(Buffer &buf) {
        if (_sinks.empty()) return;
        for (auto &sink : _sinks) {
            sink->log(buf.begin(), buf.readAbleSize());
        }
    }

private:
    AsyncLooper::ptr _looper; // 指向异步工作器的智能指针
};

// LoggerBuilder类用于构建Logger对象，采用建造者模式来组织Logger的各个组成部分
class LoggerBuilder {
public:
    // 构造函数，初始化日志器类型为同步（LOGGER_SYNC ），日志等级为DEBUG
    LoggerBuilder() :
        _logger_type(LoggerType::LOGGER_SYNC),
        _limit_level(LogLevel::value::DEBUG),
        _looper_type(AsyncType::ASYNC_SAFE) {}

    // 设置日志器类型
    void buildLoggerType(LoggerType type) { 
        _logger_type = type; 
    }
    //默认是安全模式，转成非安全模式
    void buildEnableUnSafeAsync(){_looper_type=AsyncType::ASYNC_UNSAFE;}
    // 设置日志器名称
    void buildLoggerName(const std::string &name) { 
        _logger_name = name; 
    }
    // 设置日志等级
    void buildLoggerLevel(LogLevel::value level) { 
        _limit_level = level; 
    }
    // 根据给定的格式化模式字符串创建并设置Formatter对象
    void buildFormatter(const std::string &pattern) {
        _formatter = std::make_shared<Formatter>(pattern);
    }
    // 模板函数，根据传入的参数创建并添加一个LogSink对象到_sinks向量中
    template <typename SinkType, typename...Args>
    void buildSink(Args &&...args) {
        // 使用SinkFactory创建一个SinkType类型的LogSink对象
        LogSink::ptr psink = SinkFactory::create<SinkType>(std::forward<Args>(args)...);
        _sinks.push_back(psink);
    }
    // 纯虚函数，由派生类实现具体的构建逻辑，用于最终构建Logger对象
    virtual Logger::ptr build() = 0; 
protected:
    LoggerType _logger_type; // 存储日志器类型，如同步或异步
    std::string _logger_name; // 存储日志器名称
    LogLevel::value _limit_level; // 存储日志等级
    Formatter::ptr _formatter; // 指向Formatter对象的智能指针，用于日志格式化
    std::vector<LogSink::ptr> _sinks; // 存储LogSink对象指针的向量，代表不同的日志落地模块
    AsyncType _looper_type;
};

// 2. 派生出具体的建造者类---局部日志器的建造者 & 全局的日志器建造者（后边添加了全局单例管理器之后，将日志器添加全局管）
class LocalLoggerBuilder : public LoggerBuilder {
public:
    // 重写基类的build函数，用于构建并返回一个Logger对象
    Logger::ptr build() override {
        // 断言日志器名称不能为空，确保日志器必须有名称
        assert(_logger_name.empty() == false); 
        // 如果Formatter对象为空，则创建一个默认的Formatter对象
        if (_formatter.get() == nullptr) {
            _formatter = std::make_shared<Formatter>();
        }
        // 如果_sinks向量为空，添加一个标准输出的LogSink对象（StdoutSink ）
        if (_sinks.empty()) {
            buildSink<StdoutSink>();
        }
        // 如果日志器类型为异步（LOGGER_ASYNC ），这里后续可能需要添加异步相关的完整逻辑
        if (_logger_type == LoggerType::LOGGER_ASYNC) {
            return std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
        }
        // 使用给定的参数创建并返回一个同步Logger对象（SyncLogger ）
        return std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
    }
};


//     日志器管理器：
// 作用 1：对所有创建的日志器进行管理
// 特性：将管理器设计为单例
// 作用 2：可以在程序的任意位置，获取相同的单例对象，获取其中的日志器进行日志输出
// 拓展：单例管理器创建的时候，默认先创建一个日志器（用于进行标准输出的打印）
// 目的：让用户在不创建任何日志器的情况下，也能进行标准输出的打印，方便用户使用
// 设计：
// 管理的成员：
// 1/默认日志器
// 2/所管理的日志器数组
// 3/互斥锁
// 提供的接口：
// 1/添加日志器管理
// 2/判断是否管理了指定名称的日志器
// 3/获取指定名称的日志器
// 4/获取默认日志器
class LoggerManager {
public:
    // 获取单例对象的静态函数
    static LoggerManager& getInstance() {
        // 在C++11之后，针对静态局部变量，编译器在编译的层面实现了线程安全
        // 当静态局部变量在没有构造完成之前，其他的线程进入就会阻塞
        static LoggerManager eton; 
        return eton;
    }

    // 添加日志器到管理器的函数
    void addLogger(Logger::ptr &logger) {
        // 如果已经存在同名日志器，则直接返回
        if (hasLogger(logger->name())) return ; 
        std::unique_lock<std::mutex> lock(_mutex);
        // 将日志器插入到_loggers映射中，以日志器名称为键
        _loggers.insert(std::make_pair(logger->name(), logger)); 
    }

    // 判断是否管理了指定名称的日志器
    bool hasLogger(const std::string &name) {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if (it == _loggers.end()) {
            return false;
        }
        return true;
    }

    // 获取指定名称的日志器
    Logger::ptr getLogger(const std::string &name) {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _loggers.find(name);
        if (it == _loggers.end()) {
            return Logger::ptr();
        }
        return it->second;
    }

    // 获取默认日志器
    Logger::ptr rootLogger() {
        return _root_logger;
    }

private:
    // 构造函数，用于初始化默认日志器等操作
    LoggerManager() {
        std::unique_ptr<logsys::LoggerBuilder> builder(new logsys::LocalLoggerBuilder());
        builder->buildLoggerName("root");
        _root_logger = builder->build();
        _loggers.insert(std::make_pair("root", _root_logger));
    }

private:
    std::mutex _mutex; // 互斥锁，用于保证多线程环境下对_loggers的操作安全
    Logger::ptr _root_logger; // 默认日志器
    std::unordered_map<std::string, Logger::ptr> _loggers; // 存储管理的日志器，以名称为键
};

// 设计一个全局日志器的建造者--在局部的基础上增加了一个功能: 将日志器添加到的单例对象中
class GlobalLoggerBuilder : public LoggerBuilder {
public:
    // 重写build函数，用于构建日志器
    Logger::ptr build() override {
        // 断言日志器名称不能为空，必须有日志器名称
        assert(_logger_name.empty() == false); 
        // 如果格式化器指针为空，则创建一个默认的格式化器
        if (_formatter.get() == nullptr) { 
            _formatter = std::make_shared<Formatter>();
        }
        // 如果日志落地模块列表为空，则添加一个标准输出的日志落地模块
        if (_sinks.empty()) { 
            buildSink<StdoutSink>();
        }
        Logger::ptr logger;
        // 根据日志器类型判断创建异步日志器还是同步日志器
        if (_logger_type == LoggerType::LOGGER_ASYNC) {
            logger = std::make_shared<AsyncLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
        } else {
            logger = std::make_shared<SyncLogger>(_logger_name, _limit_level, _formatter, _sinks);
        }
        // 将构建好的日志器添加到LoggerManager的单例对象中，实现全局管理
        LoggerManager::getInstance().addLogger(logger); 
        return logger;
    }
};

}
#endif